//
//  XGCalendar.m
//  SACalendar
//
//  Created by YaoZhaofeng on 15/4/16.
//  Copyright (c) 2015年 SyncoApp. All rights reserved.
//

#import "XGCalendar.h"
#import "XGCalendarCell.h"
#import "DMLazyScrollView.h"
#import "DateUtil.h"
#import "NSDate+Description.h"
#import "XGCalendarHeaderView.h"

#define ARC4RANDOM_MAX	0x100000000

@interface XGCalendar ()<UICollectionViewDataSource, UICollectionViewDelegate,DMLazyScrollViewDelegate,XGCalendarHeaderViewDataSource,XGCalendarHeaderViewDelegate,UITableViewDataSource,UITableViewDelegate>{
    DMLazyScrollView* scrollView;
    NSMutableDictionary *controllers;
    NSMutableDictionary *calendars;
    
    int year, month;
    int prev_year, prev_month;
    int next_year, next_month;
    int current_date, current_month, current_year;
    
    int state, scroll_state;
    int previousIndex;
    BOOL scrollLeft;
    
    CGSize cellSize;
    
    int headerSize;
}

@property (nonatomic, strong) UILabel *monthLabel;
@property (nonatomic, strong) XGCalendarHeaderView *headerView;
@property (nonatomic, strong) UITableView *table;

@end

@implementation XGCalendar

- (id)initWithFrame:(CGRect)frame
{
    return [self initWithFrame:frame month:0 year:0 scrollDirection:ScrollDirectionHorizontal pagingEnabled:YES];
}

- (id)initWithFrame:(CGRect)frame month:(int)m year:(int)y
{
    return [self initWithFrame:frame month:m year:y scrollDirection:ScrollDirectionHorizontal pagingEnabled:YES];
}

-(id)initWithFrame:(CGRect)frame
   scrollDirection:(scrollDirection)direction
     pagingEnabled:(BOOL)paging
{
    return [self initWithFrame:frame month:0 year:0 scrollDirection:direction pagingEnabled:paging];
}

-(id)initWithFrame:(CGRect)frame
             month:(int)m year:(int)y
   scrollDirection:(scrollDirection)direction
     pagingEnabled:(BOOL)paging
{
    self = [super initWithFrame:frame];
    if (self) {
        
        _calendar = [NSCalendar currentCalendar];
        [_calendar setLocale:[NSLocale currentLocale]];
        _date = [NSDate date];
        
        headerSize = 70.0f;
        self.headerView = [[XGCalendarHeaderView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, headerSize)];
        _headerView.dataSource = self;
        _headerView.delegate = self;
        [self addSubview:_headerView];
        
        controllers = [NSMutableDictionary dictionary];
        calendars = [NSMutableDictionary dictionary];
        
        self.displayMode = XGCalendarViewModeMonth;
        state = LOADSTATESTART;
        scroll_state = SCROLLSTATE_120;
        
        current_date = [[DateUtil getCurrentDate]intValue];
        current_month = [[DateUtil getCurrentMonth]intValue];
        current_year = [[DateUtil getCurrentYear]intValue];
        
        if (m == 0 && y == 0) {
            month = current_month;
            year = current_year;
        }
        else{
            month = m;
            year = y;
        }
        
        CGRect rect = CGRectMake(0, headerSize, self.frame.size.width, self.frame.size.height - headerSize);
        scrollView = [[DMLazyScrollView alloc] initWithFrameAndDirection:rect direction:direction circularScroll:YES paging:paging];
        scrollView.controlDelegate = self;
        
        self.backgroundColor = viewBackgroundColor;
        
        [self addObserver:self forKeyPath:@"delegate" options:NSKeyValueObservingOptionNew context:nil];
        
        self.monthLabel = [[UILabel alloc]initWithFrame:CGRectMake(50, 0, self.frame.size.width - 100, headerSize)];
        [_monthLabel setBackgroundColor:[UIColor clearColor]];
        _monthLabel.font = [UIFont systemFontOfSize: 20];
        _monthLabel.textAlignment = NSTextAlignmentCenter;
        _monthLabel.textColor = headerTextColor;
        _monthLabel.userInteractionEnabled = YES;
        [self addSubview:_monthLabel];
        
        UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(test:)];
        gesture.numberOfTapsRequired = 1;
        [_monthLabel addGestureRecognizer:gesture];
        
        _table = [UITableView new];
        _table.tableFooterView = [UIView new];
        [_table setDelegate:self];
        [_table setDataSource:self];
        [self addSubview:_table];
    }
    
    __weak __typeof(&*self)weakSelf = self;
    scrollView.dataSource = ^(NSUInteger index) {
        return [weakSelf controllerAtIndex:index];
    };
    scrollView.numberOfPages = 3;
    [self addSubview:scrollView];
    
    return self;
    
}

#pragma mark - local methods

- (void)setDisplayMode:(XGCalendarDisplayMode)displayMode
{
    [self setDisplayMode:displayMode animated:NO];
}

- (void)setDisplayMode:(XGCalendarDisplayMode)displayMode animated:(BOOL)animated
{
    if (self.displayMode == displayMode) {
        return;
    }
    if (displayMode == XGCalendarViewModeMonth) {
        CGRect rect = CGRectMake(0, headerSize, self.frame.size.width, self.frame.size.height - headerSize);
        [scrollView setFrame:rect];
        [_table setAlpha:0.0f];
    }else{
        CGRect rect = CGRectMake(0, headerSize, self.frame.size.width, (self.frame.size.height - headerSize)/MAX_WEEK);
        [scrollView setFrame:rect];
        [_table setAlpha:1.0f];
    }
    _displayMode = displayMode;
    [_table setFrame:CGRectMake(0, CGRectGetMaxY(scrollView.frame), self.frame.size.width, self.frame.size.height -scrollView.frame.size.height - headerSize )];
    for (UICollectionView *calendar in calendars.allValues) {
        CGRect rect = CGRectMake(0, 0, self.frame.size.width, CGRectGetHeight(scrollView.frame));
        [calendar setFrame:rect];
        [calendar reloadData];
    }
}

- (void)test:(id)sender{
    [self setDisplayMode:XGCalendarViewModeMonth];
}


- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
    if (nil != _delegate && [_delegate respondsToSelector:@selector(XGCalendar:didDisplayCalendarForMonth:year:)]) {
        [_delegate XGCalendar:self didDisplayCalendarForMonth:month year:year];
    }
}

- (NSDate *)nextSelectDateToLoad{
    NSDate *date = [self date];
    if (self.displayMode == XGCalendarViewModeMonth) {
        NSUInteger maxDays = [[self calendar] daysPerMonthUsingReferenceDate:date];
        NSUInteger todayInMonth =[[self calendar] daysInDate:date];
        
        //  If we're the last day of the month, just roll over a day
        if (maxDays == todayInMonth) {
            date = [[self calendar] dateByAddingDays:1 toDate:date];
        }
        //  Otherwise, add a month and then go to the first of the month
        else{
            NSUInteger day = [[self calendar] daysInDate:date];
            date = [[self calendar] dateByAddingMonths:1 toDate:date];              //  Add a month
            date = [[self calendar] dateBySubtractingDays:day-1 fromDate:date];     //  Go to the first of the month
        }
        
        //  If today is in the visible month, jump to today
        if([[self calendar] date:date isSameMonthAs:[NSDate date]]){
            date = [NSDate date];
        }
    }
    else if (self.displayMode == XGCalendarViewModeWeek){
        date = [[self calendar] dateByAddingWeeks:1 toDate:date];                   //  Add a week
        
        NSUInteger dayOfWeek = [[self calendar] weekdayInDate:date];
        date = [[self calendar] dateBySubtractingDays:dayOfWeek-1 fromDate:date];   //  Jump to sunday
        
        //  If today is in the visible week, jump to today
        if ([[self calendar] date:date isSameWeekAs:[NSDate date]]) {
            date = [NSDate date];
        }
    }
    
    return date;
}

- (NSDate *)preSelectDateToLoad{
    NSDate *date = [self date];
    if (self.displayMode == XGCalendarViewModeMonth) {
        NSUInteger day = [[self calendar] daysInDate:date];
        
        date = [[self calendar] dateBySubtractingMonths:1 fromDate:date];       //  Subtract a month
        date = [[self calendar] dateBySubtractingDays:day-1 fromDate:date];     //  Go to the first of the month
        
        //  If today is in the visible month, jump to today
        if([[self calendar] date:date isSameMonthAs:[NSDate date]]){
            date = [NSDate date];
        }
    }
    else if (self.displayMode == XGCalendarViewModeWeek){
        date = [[self calendar] dateBySubtractingWeeks:1 fromDate:date];               //  Subtract a week
        
        NSUInteger dayOfWeek = [[self calendar] weekdayInDate:date];
        date = [[self calendar] dateBySubtractingDays:dayOfWeek-1 fromDate:date];   //  Jump to sunday
        
        //  If today is in the visible week, jump to today
        if ([[self calendar] date:date isSameWeekAs:[NSDate date]]) {
            date = [NSDate date];
        }
    }
    return date;
}

/**
 *get the date in the loading month
 */

- (NSDate *)dateToLoad:(int)tag{
    NSDate *date = [self date];
    if (scroll_state == SCROLLSTATE_120) {
        if (tag == 0){
            //next
            date = [self nextSelectDateToLoad];
        }
        else if(tag == 1){
            //pre
            date = [self preSelectDateToLoad];
        }
    }
    else if(scroll_state == SCROLLSTATE_201){
        if(tag == 1){
            //next
            date = [self nextSelectDateToLoad];
        }
        else if(tag ==2){
            //pre
            date = [self preSelectDateToLoad];
        }
        else{
            
        }
    }
    else{
        if (tag == 0){
            //pre
            date = [self preSelectDateToLoad];
        }
        else if(tag == 1){
            
        }
        else{
            //next
            date = [self nextSelectDateToLoad];
        }
    }
    
    return date;
}

- (NSString *)titleForHeader
{
    return [[self date] monthAndYearOnCalendar:[self calendar]];
}

#pragma mark -XGCalendarHeaderViewDataSource

- (NSString *)titleForHeader:(XGCalendarHeaderView *)header{
    return [[self date] monthAndYearOnCalendar:[self calendar]];
}


#pragma mark -XGCalendarHeaderViewDelegate

- (void)forwardTapped{
    [scrollView moveByPages:1 animated:NO];
}
- (void)backwardTapped{
    [scrollView moveByPages:-1 animated:NO];
}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [[self dataSource] XGCalendarTableView:tableView numberOfRowsInSection:section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [[self dataSource] XGCalendarTableView:tableView cellForRowAtIndexPath:indexPath];
}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
    if ([self.delegate respondsToSelector:@selector(XGCalendarTableView:canEditRowAtIndexPath:)]) {
        return [self.delegate XGCalendarTableView:tableView canEditRowAtIndexPath:indexPath];
    }else{
        return NO;
    }
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([self.delegate respondsToSelector:@selector(XGCalendarTableView:commitEditingStyle:forRowAtIndexPath:)]) {
        return [self.delegate XGCalendarTableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath];
    }
}

#pragma mark - UITableViewDelegate

- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath{
    if ([self.delegate respondsToSelector:@selector(XGCalendarTableView:titleForDeleteConfirmationButtonForRowAtIndexPath:)]) {
        return [self.delegate XGCalendarTableView:tableView titleForDeleteConfirmationButtonForRowAtIndexPath:indexPath];
    }else{
        return @"";
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [[self delegate] XGCalendarTableView:tableView didSelectRowAtIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 55.0f;
}

#pragma mark - DMLLazyScrollView dataSource

- (UIViewController *) controllerAtIndex:(NSInteger) index {
    /*
     * Handle right scroll
     */
    if (index == previousIndex && state == LOADSTATEPREVIOUS) {
        if (++month > MAX_MONTH) {
            month = MIN_MONTH;
            year ++;
        }
        self.date = [self nextSelectDateToLoad];
        
        scrollLeft = NO;
    }
    
    /*
     * Handle left scroll
     */
    else if(state == LOADSTATEPREVIOUS){
        if (--month < MIN_MONTH) {
            month = MAX_MONTH;
            year--;
        }
        
        self.date = [self preSelectDateToLoad];
        
        scrollLeft = YES;
    }
    
    previousIndex = (int)index;
    
    //    UIViewController *contr = [[UIViewController alloc] init];
    //    contr.view.backgroundColor = scrollViewBackgroundColor;
    //
    if (state  <= LOADSTATEPREVIOUS ) {
        state = LOADSTATENEXT;
    }
    else if(state == LOADSTATENEXT){
        prev_month = month - 1;
        prev_year = year;
        if (prev_month < MIN_MONTH) {
            prev_month = MAX_MONTH;
            prev_year--;
        }
        state = LOADSTATECURRENT;
    }
    else{
        next_month = month + 1;
        next_year = year;
        if (next_month > MAX_MONTH) {
            next_month = MIN_MONTH;
            next_year++;
        }
        
        if (scrollLeft) {
            if (--scroll_state < SCROLLSTATE_120) {
                scroll_state = SCROLLSTATE_012;
            }
        }
        else{
            scroll_state++;
            if (scroll_state > SCROLLSTATE_012) {
                scroll_state = SCROLLSTATE_120;
            }
        }
        state = LOADSTATEPREVIOUS;
        
        if (nil != _delegate && [_delegate respondsToSelector:@selector(XGCalendar:didDisplayCalendarForMonth:year:)]) {
            [_delegate XGCalendar:self didDisplayCalendarForMonth:month year:year];
        }
    }
    
    /*
     * if already exists, reload the calendar with new values
     */
    UICollectionView *calendar = [calendars objectForKey:[NSString stringWithFormat:@"%li",(long)index]];
    
    [calendar reloadData];
    
    /*
     * create new view controller and add it to a dictionary for caching
     */
    if (![controllers objectForKey:[NSString stringWithFormat:@"%li",(long)index]]) {
        UIViewController *contr = [[UIViewController alloc] init];
        contr.view.backgroundColor = scrollViewBackgroundColor;
        
        UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
        flowLayout.itemSize = scrollView.frame.size;
        
        //self.frame.size.height / calendarToHeaderRatio;
        
        CGRect rect = CGRectMake(0, 0, self.frame.size.width, CGRectGetHeight(scrollView.frame));
        UICollectionView *calendar = [[UICollectionView alloc]initWithFrame:rect collectionViewLayout:flowLayout];
        calendar.dataSource = self;
        calendar.delegate = self;
        calendar.scrollEnabled = NO;
        [calendar registerClass:[XGCalendarCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
        [calendar setBackgroundColor:calendarBackgroundColor];
        calendar.tag = index;
        
        NSString *string = @"STRING";
        CGSize size;
        if (IOS7_OR_LATER) {
            size = [string sizeWithAttributes:
                    @{NSFontAttributeName:[UIFont systemFontOfSize:12]}];
        }else{
            size = [string sizeWithFont:[UIFont systemFontOfSize:12]];
        }
        
        [contr.view addSubview:calendar];
        
        [calendars setObject:calendar forKey:[NSString stringWithFormat:@"%li",(long)index]];
        [controllers setObject:contr forKey:[NSString stringWithFormat:@"%li",(long)index]];
        
        return contr;
    }
    else{
        return [controllers objectForKey:[NSString stringWithFormat:@"%li",(long)index]];
    }
    
}


#pragma  mark - COLLECTION VIEW DELEGATE

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    [_headerView setHeaderTitle:[self titleForHeader]];
    int width = CGRectGetWidth(scrollView.frame);
    int height = CGRectGetHeight(scrollView.frame);
    cellSize = CGSizeMake(width/DAYS_IN_WEEKS,  height/ (self.displayMode == XGCalendarViewModeMonth?MAX_WEEK:1));
    if (self.displayMode == XGCalendarViewModeMonth) {
        NSDate *workDate = [self dateToLoad:(int)collectionView.tag];
        int weeksInMonth =(int) [[self calendar] weeksPerMonthUsingReferenceDate:workDate];
        
        return weeksInMonth*7;
    }
    else{
        return 7;
    }
}

/**
 *  Controls what gets displayed in each cell
 *  Edit this function for customized calendar logic
 */

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    XGCalendarCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
    
    NSDate *workDate = [self dateToLoad:(int)collectionView.tag];
    NSDate *loadingDate = workDate;
    BOOL cellRepresentsToday = NO;
    BOOL isInRange = YES;
    
    if (self.displayMode == XGCalendarViewModeMonth) {
        NSDate *firstOfTheMonth = [[self calendar] firstDayOfTheMonthUsingReferenceDate:workDate];
        
        NSDate *firstDate = [[self calendar] firstDayOfTheWeekUsingReferenceDate:firstOfTheMonth];
        
        loadingDate = [[self calendar] dateByAddingDays:indexPath.row toDate:firstDate];
        
        cellRepresentsToday = [[self calendar] date:loadingDate isSameDayAs:[NSDate date]];
        isInRange = [[self calendar] date:loadingDate isSameMonthAs:workDate];
    }
    else{
        NSDate *firstDate = [[self calendar] firstDayOfTheWeekUsingReferenceDate:workDate];
        
        loadingDate = [[self calendar] dateByAddingDays:indexPath.row toDate:firstDate];
        
        cellRepresentsToday = [[self calendar] date:loadingDate isSameDayAs:[NSDate date]];
        isInRange = [[self calendar] date:loadingDate isSameWeekAs:workDate];
    }
    
    if (!isInRange) {
        [cell setState:XGCalendarCellStateInactive];
    }
    else{
        if ([[self calendar] date:loadingDate isSameDayAs:workDate]) {
            //day selected
            if (cellRepresentsToday) {
                //today
                [cell setState:XGCalendarCellStateTodaySelected];
            }
            else{
                [cell setState:XGCalendarCellStateSelected];
            }
        }
        else{
            if (cellRepresentsToday) {
                //today
                [cell setState:XGCalendarCellStateTodayDeselected];
            }
            else if ((indexPath.row+1)%7 == 0 || indexPath.row%7 == 0){
                [cell setState:XGCalendarCellStateWeekend];
            }
            else{
                [cell setState:XGCalendarCellStateNormal];
            }
        }
        if (cellRepresentsToday) {
            [cell setNumber:[NSNumber numberWithInt:100]];
        }else{
            NSUInteger day = [[self calendar] daysInDate:loadingDate];
            [cell setNumber:@(day)];
        }
    }
    
    if (self.displayMode == XGCalendarViewModeWeek) {
        [cell showBottomLine];
    }
//    int events = [[self dataSource] XGCalendarView:self scheduleCountForDate:loadingDate]/1000;
//    BOOL showDot = indexPath.row%2 == 0;
//    [cell showDot:showDot];
    return cell;
}
/*
 * Scale the collection view size to fit the frame
 */
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return cellSize;
}

/*
 * Set all spaces between the cells to zero
 */
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
    return 0;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return 0;
}

/*
 * If the width of the calendar cannot be divided by 7, add offset to each side to fit the calendar in
 */
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    int width = self.frame.size.width;
    int offset = (width % DAYS_IN_WEEKS) / 4;
    // top, left, bottom, right
    return UIEdgeInsetsMake(0,offset,0,offset);
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    BOOL isInRange = NO;
    NSDate *selectDate = _date;
    
    if (self.displayMode == XGCalendarViewModeMonth) {
        NSDate *firstOfTheMonth = [[self calendar] firstDayOfTheMonthUsingReferenceDate:_date];
        NSDate *firstDate = [[self calendar] firstDayOfTheWeekUsingReferenceDate:firstOfTheMonth];
        selectDate = [[self calendar] dateByAddingDays:indexPath.row toDate:firstDate];
        isInRange = [[self calendar] date:selectDate isSameMonthAs:_date];
    }else{
        NSDate *firstDate = [[self calendar] firstDayOfTheWeekUsingReferenceDate:_date];
        selectDate = [[self calendar] dateByAddingDays:indexPath.row toDate:firstDate];
        isInRange = [[self calendar] date:selectDate isSameWeekAs:_date];
        
        if (isInRange) {
            [_table reloadData];
        }
    }
    
    if (isInRange) {
        self.date = selectDate;
        [collectionView reloadData];
        if (_delegate && [_delegate respondsToSelector:@selector(XGCalendar:didSelectDate:)]) {
            [_delegate XGCalendar:self didSelectDate:self.date];
        }
    }
}

/**
 *  Clean up
 */
- (void)dealloc {
    [self removeObserver:self forKeyPath:@"delegate"];
}
@end
